OS: Windows 10
Editor: Visual Studio Code
Rust version: 1.63.0
先前有一篇有比較過Rust中的String
與string literal。本篇以集合的角度來理解字串。
用集合的角度來想的話,字串其實就是字符的序列,下面開始介紹字串的常見操作。
Rust有直接支援UTF-8,所以這樣可以直接印出表情符號:
// 這會印出三杯馬丁尼
let martini_emoji = String::from("\u{1F378}\u{1F378}\u{1F378}");
println!("{}", martini_emoji);
之前使用過這個方法建立字串:
let s = String.from("hello, world");
也可以使用這樣的方法建立:
let s = "hello, world".to_string();
將string literal 轉成 String
。
幾種常見的方法更新字串的內容:
let mut s = "Hello".to_string();
// 加入字符
s.push(',');
// 加入string literal
s.push_str(" everyone!");
// 利用`format!()` 巨集
let hello_world = format!("{s} Nice to meet you");
println!("{}", hello_world);
還有一個是使用符號+
來串接:
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = s1 + "-" + &s2 + "-" + &s3;
可以發現到s1
的所有權變到s
身上,然後s2
跟s3
使用借用的形式,如果把他們嘗試輸出
Compiling basic v0.1.0 (D:\projects\rust_learning\basic)
error[E0382]: borrow of moved value: `s1`
--> src\main.rs:21:20
|
14 | let s1 = String::from("tic");
| -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
...
18 | let s = s1 + "-" + &s2 + "-" + &s3;
| -------- `s1` moved due to usage in operator
...
21 | println!("{}", s1);
| ^^ value borrowed here after move
|
note: calling this operator moves the left-hand side
--> C:\Users\User\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\core\src\ops\arith.rs:114:12
|
114 | fn add(self, rhs: Rhs) -> Self::Output;
| ^^^^
= note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)
For more information about this error, try `rustc --explain E0382`.
會標示s
已經無效了。
s1
跟s2
會是借用的原因主要來源於+
的泛型實作,參考來源於官方文件,點進去裏頭,除了看得懂十座許多泛型以外,還看到有許多沒看過的語法跟用詞,先列下來,早晚會再次碰到(挖坑~挖坑~):
trait
是甚麼?impl<'_>
是甚麼意思?let s = String::from("Good night.");
// 清除
s.clear();
雖說已經是清除了,但原本佔據的大小(capacity)不會變更
let mut s = String::from("Good night.");
println!("string capacity is {}", s.capacity());
// 清除
s.clear();
println!("string capacity is {}", s.capacity());
當然也有remove
的功能,填入索引(index)刪除字符,但要注意!如果索引超出範圍,程式會直接panic
let mut s = String::from("Good night.");
let end = s.remove(s.len() - 1);
println!("{}", end);
s.remove(12); // PANIC!
雖說前面有提到可以把字串視為字符的序列集合,但是Rust的字串不允許使用[]
進行字符的索引。
let s = String::from("Good night.");
let end = &s[s.len() - 1];
以下是輸出
Compiling basic v0.1.0 (D:\projects\rust_learning\basic)
error[E0277]: the type `String` cannot be indexed by `usize`
--> src\main.rs:3:16
|
3 | let end = &s[s.len() - 1];
| ^^^^^^^^^^^^^^ `String` cannot be indexed by `usize`
|
= help: the trait `Index<usize>` is not implemented for `String`
= help: the following other types implement trait `Index<Idx>`:
<String as Index<RangeFrom<usize>>>
<String as Index<RangeFull>>
<String as Index<RangeInclusive<usize>>>
<String as Index<RangeTo<usize>>>
<String as Index<RangeToInclusive<usize>>>
<String as Index<std::ops::Range<usize>>>
<str as Index<I>>
For more information about this error, try `rustc --explain E0277`.
可以看到不給使用[]
,如果是上面的例子的話,會覺得納悶為什麼Rust不給這樣使用,原因最開始講的,Rust預設支援UTF-8,假如像最開始印出三杯馬丁尼,那使用索引取出某個位置的字符便會出現問題。
let martini_emoji = String::from("\u{1F378}\u{1F378}\u{1F378}");
所以在字串上,不能使用[]
取指定某個位置的字符,但是可以使用切片(slice)取出string literal,但如果以馬丁尼的例子來看,也是要小心切片的位置可能構不成一個表情符號:
let martini_emoji = String::from("\u{1F378}\u{1F378}\u{1F378}");
let martini = &martini_emoji[0..2];
println!("{}", martini);
輸出:
thread 'main' panicked at 'byte index 2 is not a char boundary; it is inside '?' (bytes 0..4) of `???`', library\core\src\str\mod.rs:127:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\basic.exe` (exit code: 101)
compiler很好心的告訴你,哪裡到哪裡可以形成一個表情符號:
// SUCCESS
let martini_emoji = String::from("\u{1F378}\u{1F378}\u{1F378}");
let martini = &martini_emoji[0..4];
println!("{}", martini);